﻿using System;
using System.Text;

namespace jsonar
{
	internal struct Parser
	{
		public static Json ParseJson(string str, ref int idx)
		{
			char c;
			while (++idx < str.Length)
			{
				c = str[idx];
				if (char.IsWhiteSpace(c)) continue;
				if (c == '{') return ParseObj(str, ref idx);
				else if (c == '[') return ParseArr(str, ref idx);
				return ParseVal(str, ref idx);
			}
			return Null.INSTANCE;
		}
		public static Obj ParseObj(string str, ref int idx)
		{
			char c;
			Obj obj = new Obj();
			while (++idx < str.Length)
			{
				c = str[idx];
				if(char.IsWhiteSpace(c)) continue;
				if (c == '}') break;
				if (c == ',') continue;
				idx--; 
				string name = null;
				Json json = null;
				parseField(str, ref idx, ref name, ref json);
				if (name != null && name.Length > 0) obj.Add(name, json);
			}
			return obj;
		}
		private static void parseField(string str, ref int idx, ref string name, ref Json obj)
		{
			char c;
			while (++idx < str.Length)
			{
				c = str[idx];
				if (char.IsWhiteSpace(c)) continue;
				idx--;
				name = parseName(str, ref idx);
				break;
			}
			while (++idx < str.Length)
			{
				c = str[idx];
				if (char.IsWhiteSpace(c)) continue;
				if (c == '}' || c == ',') { idx--; break; }
				if (c == ':') continue;
				idx--; 
				obj = ParseJson(str, ref idx);
				break;
			}
		}
		private static string parseName(string str, ref int idx)
		{
			char sc = char.MinValue;
			char c = str[++idx];
			if (c == '\'' || c == '"') { sc = c; idx++; }
			int sidx = idx;
			if (sc == char.MinValue)
			{
				while (++idx < str.Length)
				{
					c = str[idx];
					if (c == ',' || c == ':' || c == '}' || char.IsWhiteSpace(c)) break;
				}
				if (idx > sidx) str = str.Substring(sidx, idx - sidx); else str = null;
				idx--;
			}
			else {
				while (++idx < str.Length)
				{
					if (str[idx] == sc)
					{
						int tidx = idx;
						while (--tidx >= sidx) { if (str[tidx] != '\\') break; }
						if (idx - tidx > 1 && (idx - tidx) % 2 == 0) continue;
						break;
					}
				}
				if (idx > sidx) str = str.Substring(sidx, idx - sidx); else str = null;
			}
			return str;
		}
		public static Arr ParseArr(string str, ref int idx)
		{
			char c;
			Arr arr = new Arr();
			while (++idx < str.Length)
			{
				c = str[idx];
				if (char.IsWhiteSpace(c)) continue;
				if (c == ']') break;
				if (c == ',') continue;
				idx--; 
				arr.Add(ParseJson(str, ref idx));
			}
			return arr;
		}
		public static Json ParseVal(string str, ref int idx)
		{
			char sc = char.MinValue;
			char c = str[idx];
			if (c == '\'' || c == '"') { sc = c; } else idx--;
			int sidx = idx+1;
			if (sc == char.MinValue)
			{
				while (++idx < str.Length)
				{
					c = str[idx];
					if (c == ',' || c == ':' || c == '}' || c == ']' || char.IsWhiteSpace(c)) break;
				}
			}
			else
			{
				while (++idx < str.Length)
				{
					c = str[idx];
					if (c == sc)
					{
						int tidx = idx;
						while (--tidx >= sidx) { if (str[tidx] != '\\') break; }
						if (idx - tidx > 1 && (idx - tidx) % 2 == 0) continue;
						break;
					}
				}
			}
			if (idx <= sidx) { if (sc == char.MinValue) { idx--; return Null.INSTANCE; } return String.EMPTY; }
			str = str.Substring(sidx, idx - sidx);
			if (sc != char.MinValue) return new String(String.ConvJson2String(str));
			idx--;
			if (str == "true") return True.INSTANCE;
			if (str == "false") return False.INSTANCE;
			if (str == "null" || str == "undefined") return Null.INSTANCE;
			int dot = 0; int hex = 0; bool s = false;
			sidx = -1;
			while (++sidx < str.Length)
			{
				c = str[sidx];
				if (c >= '0' && c <= '9') continue;
				if(c == '.') {dot++; continue;}
				if ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') || c == 'x' || c == 'X') { hex++; continue; }
				s = true; 
				break;
			}
			if (s) return new String(String.ConvJson2String(str));
			if (dot > 0) { try { return new Double(System.Convert.ToDouble(str)); } catch { } }
			else if (hex > 0) { 
				try {
                    return new Int(System.Convert.ToInt32(str, 16));
                } 
				catch(OverflowException e) {
					try { return new Long(System.Convert.ToInt64(str, 16)); } catch { }
				}
				catch{}
			}
			else {
				try {
                    return new Int(System.Convert.ToInt32(str));
                } 
				catch(OverflowException e1) {
					try { return new Long(System.Convert.ToInt64(str)); } catch { }
				}
				catch{}
			}
			return new String(String.ConvJson2String(str));
		}
	}
}
